package com.ryujung.dp.leetCode_10;

/*
 * @lc app=leetcode.cn id=10 lang=java
 *
 * [10] 正则表达式匹配
 *
 * https://leetcode-cn.com/problems/regular-expression-matching/description/
 *
 * algorithms
 * Hard (27.45%)
 * Likes:    1141
 * Dislikes: 0
 * Total Accepted:    71.1K
 * Total Submissions: 258.4K
 * Testcase Example:  '"aa"\n"a"'
 *
 * 给你一个字符串 s 和一个字符规律 p，请你来实现一个支持 '.' 和 '*' 的正则表达式匹配。
 * 
 * '.' 匹配任意单个字符
 * '*' 匹配零个或多个前面的那一个元素
 * 
 * 
 * 所谓匹配，是要涵盖 整个 字符串 s的，而不是部分字符串。
 * 
 * 说明:
 * 
 * 
 * s 可能为空，且只包含从 a-z 的小写字母。
 * p 可能为空，且只包含从 a-z 的小写字母，以及字符 . 和 *。
 * 
 * 
 * 示例 1:
 * 
 * 输入:
 * s = "aa"
 * p = "a"
 * 输出: false
 * 解释: "a" 无法匹配 "aa" 整个字符串。
 * 
 * 
 * 示例 2:
 * 
 * 输入:
 * s = "aa"
 * p = "a*"
 * 输出: true
 * 解释: 因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此，字符串 "aa" 可被视为 'a' 重复了一次。
 * 
 * 
 * 示例 3:
 * 
 * 输入:
 * s = "ab"
 * p = ".*"
 * 输出: true
 * 解释: ".*" 表示可匹配零个或多个（'*'）任意字符（'.'）。
 * 
 * 
 * 示例 4:
 * 
 * 输入:
 * s = "aab"
 * p = "c*a*b"
 * 输出: true
 * 解释: 因为 '*' 表示零个或多个，这里 'c' 为 0 个, 'a' 被重复一次。因此可以匹配字符串 "aab"。
 * 
 * 
 * 示例 5:
 * 
 * 输入:
 * s = "mississippi"
 * p = "mis*is*p*."
 * 输出: false
 * 
 */

// @lc code=start
/*
 * 给你一个字符串 s 和一个字符规律 p，请你来实现一个支持 '.' 和 '*' 的正则表达式匹配。
 * s 可能为空，且只包含从 a-z 的小写字母。
 * p 可能为空，且只包含从 a-z 的小写字母，以及字符 . 和 *。
 * '.' 匹配任意单个字符
 * '*' 匹配零个或多个前面的那一个元素
 * 动态规划
 * 
 * 思路: 
 * 1.如果为空,返回false
 * 2.创建动态规划的二维数组dp[i][j],表示s的前i个字符和p的前j个字符是否匹配
 *  范围   i:0~s.length() ,  j:0~p.length()
 * 
 */
class Solution2 {
    public static boolean isMatch(String s, String p) {
        if (s == null || p == null)
            return false;

        int n = s.length();
        int m = p.length();

        /**
         * 数组表示s的前n个字符和p的前j个字符匹配的结果
         * 取值范围: s-> [0,n],   p-> [0,m].
         */
        boolean[][] dp = new boolean[n + 1][m + 1];
        /**
         * 两个空串
         */
        dp[0][0] = true;

        /**
         * 单个空串:
         * 1.s为空, p中形式如 a*b*c* 结果为true,需要处理
         * 2.p为空, dp结果都为 false,不用处理
         */
        for (int j = 1; j <= m; j++) {
            if (p.charAt(j - 1) == '*') {
                dp[0][j] = dp[0][j - 2];
            }
        }

        /**
         * pd[0][0~m]和dp[0~n][0]的值已经确定了,下面遍历所有字符
         */
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                /**
                 * 情景1:  两字符相等,dp为前两个字符比较的结果
                 * 情景2: p字符为'.' , 匹配任意一个字符
                 */
                if (s.charAt(i - 1) == p.charAt(j - 1) || p.charAt(j - 1) == '.') {
                    dp[i][j] = dp[i - 1][j - 1];
                    continue;
                }
                /**
                 * 情景3: p字符为'*',分情况:
                 * c a
                 * a *
                 * 
                 * c b
                 * a *
                 * 
                 * aaa a b
                 * ccc . * c
                 * 
                 * ab
                 * .*
                 */
                if (p.charAt(j - 1) == '*') {
                    //检查*前面的字符是否为'.'
                    char pPre = p.charAt(j - 2);

                    if (pPre == s.charAt(i - 1)) {
                        dp[i][j] = dp[i-1][j - 1];
                    }
                    if (pPre != s.charAt(i - 1) && pPre != '.') {
                        dp[i][j] = dp[i][j - 2];
                    }
                    if (pPre == '.') {
                        dp[i][j] = true;
                    }
                }
            }
        }

        return dp[n][m];
    }

    public static void main(String[] args) {
        String s = "aa";
        String p = "aa*";
        System.out.println(isMatch(s, p));
    }
}
// @lc code=end
